//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

//
// For every instruction that set power management register (0x26, 0x3c, 0x3e),
// There's a comment with "Power Management"
// Such as:
// Power Management: Open stereo DAC
//
// Bluetooth is using for call, and only supports 8khz sample rate.
// Bluetooth playing and recording is only for testing.
//

#include <ddk.h>
#include <drvpub.h>
#include "audiocard.h"
#include "ac97codec.h"
#include "ac97link.h"

// Set codec register bits when the corresponding bits of val is set.
static void Codec_SetRegBits(int idx, UInt16 val)
{
    UInt16 value;

    Ac97_Read(idx, &value);
    value |= val;
    Ac97_Write(idx, value);
}

// Clear codec register bits when the corresponding bits of val is set.
static void Codec_ClearRegBits(int idx, UInt16 val)
{
    UInt16 value;

    Ac97_Read(idx, &value);
    value &= (~val);
    Ac97_Write(idx, value);
}

// Write codec register for special bits, using mask.
// reg[idx] = (reg[idx] & (~mask)) | val
static void Codec_WriteRegValue(int idx, UInt16 val, UInt16 mask)
{
    UInt16 value;

    Ac97_Read(idx, &value);
    value &= (~mask);
    value |= val;
    Ac97_Write(idx, value);
}

#if (defined(DEBUG_AUDIO) && DBG_POWER == 1)
void Codec_DumpPowerRegs()
{
    UInt16 reg26, reg3c, reg3e;
    Ac97_Read(0x26, &reg26);
    Ac97_Read(0x3c, &reg3c);
    Ac97_Read(0x3e, &reg3e);
    cprintf("Codec_DumpPowerRegs: 0x%02x, 0x%02x, 0x%02x\n",
        reg26, reg3c, reg3e);
}
#endif

static void Codec_SetPowerRegs(
    UInt16 powerReg26, UInt16 powerReg3c, UInt16 powerReg3e)
{
    Codec_SetRegBits(0x26, powerReg26);
    Codec_SetRegBits(0x3c, powerReg3c);
    Codec_SetRegBits(0x3e, powerReg3e);
    DBGOUT_FUNC(DBG_POWER, Codec_DumpPowerRegs());
}

static void Codec_ClearPowerRegs(
    UInt16 powerReg26, UInt16 powerReg3c, UInt16 powerReg3e)
{
    Codec_ClearRegBits(0x26, powerReg26);
    Codec_ClearRegBits(0x3c, powerReg3c);
    Codec_ClearRegBits(0x3e, powerReg3e);
    DBGOUT_FUNC(DBG_POWER, Codec_DumpPowerRegs());
}

// Open PCM link
static void CodecOpenPCMLink()
{
    // Setup the PCM configure register
    // PCM Codec Control
    // [15]1:GPIO pins configured as PCM interface and controlled by register
    // [14-13]01: PCM interface in slave mode
    // [12]0
    // [11-9]010: PCMCLK = voice DAC clock / 4 (for master mode)
    // [8]1:  VXDAC oversample rate: 64 * fs
    // [7]1: invert PCMCLK polarity
    // [6]0: normal PCMFS polarity
    // [5-4]00: Output left and right ADC data
    // [3-2]00: PCM Data Word Length: 16 bits
    // [1-0]11: PCM Data Format Select: DSP Mode
    Ac97_Write(0x36, 0xa583);

    // GPIO Pin Configuration
    // NOTE: GPIO1,3,4,5 are already enabled for PCM interface(see reg 0x36 bit15)
    Ac97_Write(0x4c, 0xffda);

    // Control clock division and muxing
    // [11-8]1011: Defined clock division ration for PCM interface
    //       and voice DAC: 1011=f/12
    Ac97_Write(0x44, 0x0b00);
//    Ac97_Write(0x2a, 0x0411);

    // Control audio ADC sample rate: 0x1f40=8000hz
    Ac97_Write(0x32, 0x1f40);

    // Control PLL clock generation
//    Ac97_Write(0x46,  0xa40c);
//    Ac97_Write(0x46,  0xa410);
//    Ac97_Write(0x46,  0xa427);
//    Ac97_Write(0x46,  0xa434);
//    Ac97_Write(0x46,  0xa445);
//    Ac97_Write(0x46,  0xa450);
//    Ac97_Write(0x46,  0xa460);
//    Ac97_Write(0x46,  0xa470);
    Ac97_Write(0x44,  0x0b00);

    // Enable ADC high-pass filter
    Ac97_Write(0x5c,  0x0008);
}

// Close PCM link
static void CodecClosePCMLink()
{
    // Setup the PCM configure register
    // PCM Codec Control
    Codec_ClearRegBits(0x36, 0x8000);

    // GPIO Pin Configuration
    // NOTE: GPIO1,3,4,5 are already enabled for PCM interface(see reg 0x36 bit15)
//    Ac97_Write(0x4c, 0xffda);
}

ECode Codec_OpenAc97DAC(AudioContext *pCxt)
{
    // Power Management: Open PR6, PR5, PR4, PR3, PR2, PR1
    Codec_ClearRegBits(0x26, 0x7e00);
    // Power Management: Open VREF, PLL, DACL, DACR
    Codec_ClearRegBits(0x3c, 0x06c0);
    return NOERROR;
}

ECode Codec_OpenAc97ADC(AudioContext *pCxt)
{
    // Power Management:  Open PR5, PR4, PR3, PR2, PR0
    Codec_ClearRegBits(0x26, 0x3d00);
    // Power Management: Open VREF, PLL, ADCL, ADCR
    Codec_ClearRegBits(0x3c, 0x0630);

    return NOERROR;
}

// BUGBUG: I wrote the codes following the document. When using hardware,
//  I found that left recording volume and right recording volume
//  (see register 12h) are reverse. But because they have the same volume now,
//  so I make them untouched, and still follow the document.
ECode Codec_SetAudioInSource(AudioContext *pCxt, UInt32 channel, Byte source)
{
    ECode ec = NOERROR;

    DBGOUT(DBG_LOG, ("Codec_SetAudioInSource, channel = %d, "\
        "source = %d, state = 0x%x\n", channel, source, pCxt->state));

    if (channel == AICHANNEL_CLOSE) {
        // Close all of the audio in source
        Ac97_Write(0x10,  0x00d8); // Mute MICA & MICB
        Ac97_Write(0x12,  0x8000); // Mute ADC input
        Ac97_Write(0x5c,  0x0000); // Disable ADC high-pass filter
        Ac97_Write(0x22,  0x0040); // Mic input configuration
        Ac97_Write(0x14,  0xd600); // Mute record mux to mixer path
        return NOERROR;
    }

    // Enable ADC high-pass filter
    Ac97_Write(0x5c,  0x0008);

    switch (source) {
        case AUDIOIN_MAINMIC: // Main MIC on phone
            // Power Management: Open MIC PGA
            Codec_ClearRegBits(0x3e, 0x000f);
            // MICB volume
            Codec_WriteRegValue(0x0e,  0x0000, 0x001f);
            // Main mic on phone. MICCM->MICB. Set MICB pre-amp gain control
            Codec_WriteRegValue(0x22,  0xb040, 0xf340);
            if (channel == AICHANNEL_LEFT) {
                // Power Management: Open left ADC
                Codec_ClearRegBits(0x3c, 0x0020);
                // Left recorder selector (MICB).
                Codec_WriteRegValue(0x14,  0x0048, 0x0078);
                // Open Audio ADC. Set left ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0xff00);
            }
            else { // channel == AICHANNEL_RIGHT
                // Power Management: Open right DAC
                Codec_ClearRegBits(0x3c, 0x0010);
                // Right recorder selector (MICB).
                Codec_WriteRegValue(0x14,  0x0041, 0x0047);
                // Open Audio ADC. Set right ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0x807f);
            }
            break;

        case AUDIOIN_HEADSET: // Normal headset
            Codec_GetHeadsetStatus(pCxt);
            if (!(pCxt->auxState & AUXSTATE_HEADSETPLUG)) {
                DBGOUT(DBG_ERR, ("Codec_SetAudioInSource, Can't open "\
                    "headset mic when it isn't plugged.\n"));
                ec = E_ACCESS_DENIED;
                break;
            }
            // Power Management: Open MIC PGA
            Codec_ClearRegBits(0x3e, 0x000f);
            // MICA volume
            Codec_WriteRegValue(0x0e,  0x0000, 0x1f00);
            // Mic on headset1. MIC1->MICA. Set MICA pre-amp gain control
            Codec_WriteRegValue(0x22,  0x8040, 0xfc40);
            if (channel == AICHANNEL_LEFT) {
                // Power Management: Open left ADC
                Codec_ClearRegBits(0x3c, 0x0020);
                // Left recorder selector (MICA).
                Codec_WriteRegValue(0x14,  0x0040, 0x0078);
                // Open Audio ADC. Set left ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0xff00);
            }
            else { // channel == AICHANNEL_RIGHT
                // Power Management: Open right DAC
                Codec_ClearRegBits(0x3c, 0x0010);
                // Right recorder selector (MICA).
                Codec_WriteRegValue(0x14,  0x0040, 0x0047);
                // Open Audio ADC. Set right ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0x807f);
            }
            break;

#ifdef BLUETOOTH_PLAY_RECORD_ENABLE
        case AUDIOIN_BLUETOOTH: // Bluetooth headset
            CodecOpenPCMLink();
            // Open the pass from VXDAC to MONO
            // From PCM link---VXDAC---MONO mixer---ADC---AC97 link
            //
            // Power Management: Open VXDAC and mono mixer
            // (left and right ADC already open in Codec_OpenAc97ADC)
            Codec_ClearRegBits(0x3c, 0x1001);
            // Open VXDAC to mono mixer path, and set gain
            // Codec_WriteRegValue(codec, 0x0000, 0x00f0);
            Codec_WriteRegValue(0x18, 0x0040, 0x00f0);
            // MONO PGA input select: MONOMIX
            //Codec_WriteRegValue(0x1c, 0x8000, 0xc000);
            if (channel == AICHANNEL_LEFT) {
                // Left recorder selector (MONOMIX).
                // Without 20db gain for the ADC record path
                Codec_WriteRegValue(0x14,  0x0030, 0x0078);
                // Open Audio ADC. Set left ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0xff00);
            }
            else { // channel == AICHANNEL_RIGHT
                // Right recorder selector (MONOMIX).
                // Without 20db gain for the ADC record path
                Codec_WriteRegValue(0x14,  0x0006, 0x0047);
                // Open Audio ADC. Set right ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0x807f);
            }
            break;
#endif

        case AUDIOIN_LINE1: // Phone voice from GSM module
            if (channel == AICHANNEL_RIGHT) {
                // LINER isn't used now
                ec = E_ACCESS_DENIED;
                break;
            }
            // Power Management: Open LINEL PGA
            Codec_ClearRegBits(0x3e, 0x0040);
            // Left recorder selector (LINEL).
            Codec_WriteRegValue(0x14,  0x0050, 0x0078);
            // Open Audio ADC. Set left ADC Recording volume
            Codec_WriteRegValue(0x12,  0x0000, 0xff00);
            break;

        case AUDIOIN_LINE2: // Phone voice from GSM module using headset
            if (!(pCxt->auxState & AUXSTATE_HEADSETPLUG)) {
                DBGOUT(DBG_ERR, ("Codec_SetAudioInSource, Can't get "\
                    "phone voice from headset when it isn't plugged.\n"));
                ec = E_ACCESS_DENIED;
                break;
            }
            // Power Management: Open MONOIN PGA
            Codec_ClearRegBits(0x3e, 0x0010);
            if (channel == AICHANNEL_LEFT) {
                // Left recorder selector (MONOIN).
                Codec_WriteRegValue(0x14,  0x0058, 0x0078);
                // Open Audio ADC. Set left ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0xff00);
            }
            else { // channel == AICHANNEL_RIGHT
                // Right recorder selector (MONOIN).
                Codec_WriteRegValue(0x14,  0x0043, 0x0047);
                // Open Audio ADC. Set right ADC Recording volume
                Codec_WriteRegValue(0x12,  0x0000, 0x807f);
            }
            break;

        default:
            ec = E_NOT_IMPLEMENTED;
            break;
    }

    if (SUCCEEDED(ec)) {
        Codec_SetRecordVolume(pCxt, pCxt->aiVolLeft, pCxt->aiVolRight);
    }

    return ec;
}

ECode Codec_OpenNormalCall(AudioContext *pCxt)
{
    // Open MIC BIAS
    Codec_SetRegBits(0x22, 0x0040);
    Codec_ClearRegBits(0x26, 0x0400); // PR2
    Codec_ClearRegBits(0x3e, 0x4000); // MICBIAS

    return NOERROR;
}

void Codec_CloseNormalCall(AudioContext *pCxt)
{
#ifndef MICBIAS_AUTOOPEN
    // Close MIC BIAS
    Codec_ClearRegBits(0x22, 0x0040);
    Codec_SetRegBits(0x3e, 0x4000);
#endif
    if (!(pCxt->state & STATE_AUDIOIN)) {
        Codec_SetRegBits(0x26, 0x0400); // PR2
        Ac97_Close(pCxt, AUXSTATE_ACLINK_AUDIO);
    }
}

ECode Codec_OpenHandFreeCall(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;

    // LINEL---SPEAKER MIXER---SPKL/SPKR
    //
    // Open PR6, PR3, PR2
    powerReg26 = 0x4c00;
    // Open VREF, SPKX
    powerReg3c = 0x0402;
    // Open MICBIAS, SPKL, SPKR, LL
    powerReg3e = 0x41c0;

    // Power Management
    Codec_ClearPowerRegs(powerReg26, powerReg3c, powerReg3e);

    // Open line path to speaker and set LINEL input gain
    Codec_WriteRegValue(0x0a, 0x0000, 0x5f1f);
    // INV2: SPKMIX
    Codec_WriteRegValue(0x1e, 0x0800, 0xfc00);
    // SPKL & SPKR PGA input select (INV1 & INV2)
    // Actually INV1 isn't used
    Codec_WriteRegValue(0x1c, 0x2400, 0x3f00);
    // Open MICBIAS
    Codec_SetRegBits(0x22, 0x0040);

    Codec_OpenAmplifier(pCxt, TRUE);

    return NOERROR;
}
void Codec_CloseHandFreeCall(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;

    // Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_SPEAKER);
    Ac97_Write(0x02, 0x8080);

    // Close line path to speaker
    Codec_SetRegBits(0x0a,  0x4000);

    if (pCxt->state & STATE_AUDIOIN) {
        powerReg26 = 0x4000; // Close PR6
        powerReg3c = 0x0002; // Close SPKX
        powerReg3e = 0x0180; // Close SPKL, SPKR
        if (!(AUDIOIN_LINE1 == AUDIOIN_GETLEFTSOURCE(pCxt->state))) {
            powerReg3e |= 0x0040; // Close LL
        }
    }
    else {
        powerReg26 = 0x4000; // Close PR6
        powerReg3c = 0x0002; // Close SPKX
        powerReg3e = 0x0180; // Close SPKL, SPKR
#ifndef MICBIAS_AUTOOPEN
        // Close MICBIAS
        Codec_ClearRegBits(0x22, 0x0040);
        powerReg26 |= 0x0400; // PR2
        powerReg3e |= 0x4000; // MICBIAS
#endif
    }
    // Power Management
    Codec_SetPowerRegs(powerReg26, powerReg3c, powerReg3e);

    Codec_OpenAmplifier(pCxt, FALSE);

    if (!(pCxt->state & STATE_AUDIOIN)) {
        Ac97_Close(pCxt, AUXSTATE_ACLINK_AUDIO);
    }
}

ECode Codec_OpenBluetoothCall(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;

    // Open PR6, PR5, PR4, PR3, PR2, PR0
    powerReg26 = 0x7d00;
    // Open VXDAC, VREF, PLL, ADCL, ADCR, MX
    powerReg3c = 0x1631;
    // Open MONO and MOIN
    powerReg3e = 0x2010;

    // Power Management
    Codec_ClearPowerRegs(powerReg26, powerReg3c, powerReg3e);

    CodecOpenPCMLink();

    // Open the pass From MONOIN to PCM Link
    // Transfer BCM2121's output(MONOIN) to PCM interface
    // MONOIN---14h---ADC---PCM link
    //
    // Control MONOIN to ADC path
    // [5-3]011: Left record mux: MONOIN
    // [2-0]011: Right record mux: MONOIN
    // Codec_WriteRegValue(0x14, 0x005b, 0x007f);
    // without 20db gain for the ADC record path
    Codec_WriteRegValue(0x14, 0x001b, 0x007f);
    // Add  20db gain for the ADC record path
    // Codec_WriteRegValue(0x14, 0x005b, 0x007f);
    
    // Record volume
    Ac97_Write(0x12, 0x0000);

    // Open the pass from VXDAC to MONO
    // Transfer PCM interface to BCM2121's input
    // PCM link---VXDAC---MONO mixer---1ch---MONO
    //
    // Control the output signal of the voice DAC
    // Open VXDAC to mono mixer path (connected with GSM module)
    // Codec_WriteRegValue(0x18, 0x0000, 0x00f0);
    Codec_WriteRegValue(0x18, 0x0040, 0x00f0);

    // MONO PGA input select: MONOMIX
    Codec_WriteRegValue(0x1c, 0x8000, 0xc000);

    // MONO volume
    Codec_WriteRegValue(0x08, 0x0007, 0x00ff);

    //The following is added to cancel the noise every 9 seconds.
    Ac97_Write(0x5c,  0x0008);

    return NOERROR;
}

void Codec_CloseBluetoothCall(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;

    CodecClosePCMLink();

    // Mute ADC to mono mixer path
    Codec_WriteRegValue(0x14, 0x003f, 0x007f);
    // Mute ADC input
    Codec_SetRegBits(0x12, 0x8000);
    // Mute VXDAC to mono mixer path (connected with GSM module)
    Codec_SetRegBits(0x18, 0x0080);
    // Mute MONO volume
    Codec_SetRegBits(0x08, 0x0080);
    // Disable ADC high-pass filter
    Ac97_Write(0x5c,  0x0000);

    // Close PR6, PR2, PR0
    powerReg26 = 0x4500;
    // Close VXDAC, ADCL, ADCR, MX
    powerReg3c = 0x1031;
    // Close MONO and MOIN
    powerReg3e = 0x2010;
    // Power Management
    Codec_SetPowerRegs(powerReg26, powerReg3c, powerReg3e);

    Ac97_Close(pCxt, AUXSTATE_ACLINK_AUDIO);
}

ECode Codec_OpenHeadsetCall(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;

    if (!(pCxt->auxState & AUXSTATE_HEADSETPLUG)) {
        DBGOUT(DBG_ERR, ("Codec_OpenHeadsetCall, Can't make call when "\
            "headset isn't plugged. auxState = 0x%x\n", pCxt->auxState));
        return E_ACCESS_DENIED;
    }

    // Open PR6, PR3, PR2
    powerReg26 = 0x4c00;
    // Open VREF, HPLX, HPRX, MX
    powerReg3c = 0x040d;
    // Open MICBIAS, MONO, HPL, HPR, MOIN, mic PGAs(BUGBUG)
    powerReg3e = 0x661f;

    // Power Management
    Codec_ClearPowerRegs(powerReg26, powerReg3c, powerReg3e);

    // Open the pass From MONOIN to headset
    // (AUXSPP)---MONOIN---HPL/HPR MIXER---1ch---HPL/HPR
    //
    // Open MOMOIN to headphone mixer path and set MONOIN input gain
    Codec_WriteRegValue(0x08, 0x0800, 0x9f00);
    // HPL/HPR PGA input select: HPMIXL/HPMIXR
    Codec_WriteRegValue(0x1c, 0x00a0, 0x00f0);

    // Open the pass from MIC1 to MONO
    // MIC1---MONO mixer---1ch---MONO---(AUXMIC)
    //
    // MICA volume
    Codec_WriteRegValue(0x0e,  0x0800, 0x1f00); // TAG01
    // Mic on headset1. MIC1->MICA. Set MICA pre-amp gain control
    // Open MICBIAS
    Codec_WriteRegValue(0x22,  0x8040, 0xfc40);
    // MONO mixer input select: MICA
    Codec_WriteRegValue(0x10,  0x0000, 0x00a0); // TAG01
    // Open MONO and set MONO volume
    Codec_WriteRegValue(0x08,  0x0000, 0x00ff);
    //Codec_WriteRegValue(0x08, 0x0007, 0x00ff);
    // MONO PGA input select: MONOMIX
    Codec_WriteRegValue(0x1c, 0x8000, 0xc000);

    //The following is added to cancel the noise every 9 seconds.
    Ac97_Write(0x5c,  0x0008);

    return NOERROR;
}

void Codec_CloseHeadsetCall(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;

    Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_MONO);
    // Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_HEADSET);
    Ac97_Write(0x04, 0x8080);

    // Mute MOMOIN to headphone mixer path, mute MONO
    Codec_SetRegBits(0x08, 0x4080);
    // HPL/HPR PGA input select: unused
    Codec_WriteRegValue(0x1c, 0x00f0, 0x00f0);
    // Mute MICA to mono mixer path
    Codec_WriteRegValue(0x10,  0x0080, 0x00a0);
    // Disable ADC high-pass filter
    Ac97_Write(0x5c,  0x0000);

    if (pCxt->state & STATE_AUDIOIN) {
        powerReg26 = 0x4000; // Close PR6
        powerReg3c = 0x000d; // Close HPLX, HPRX, MX
        powerReg3e = 0x2600; // Close MONO, HPL, HPR
        if (!(AUDIOIN_LINE1 == AUDIOIN_GETLEFTSOURCE(pCxt->state))) {
            powerReg3e |= 0x0040; // Close LL
        }
    }
    else {
        powerReg26 = 0x4000; // Close PR6
        powerReg3c = 0x000d; // Close HPLX, HPRX, MX
        // Close MONO, HPL, HPR, MOIN, mic PGAs(BUGBUG)
        powerReg3e = 0x261f;
#ifndef MICBIAS_AUTOOPEN
        // Close MICBIAS
        Codec_ClearRegBits(0x22, 0x0040);
        powerReg26 |= 0x0400; // PR2
        powerReg3e |= 0x4000; // MICBIAS
#endif
    }

    // Power Management
    Codec_SetPowerRegs(powerReg26, powerReg3c, powerReg3e);

    if (!(pCxt->state & STATE_AUDIOIN)) {
        Ac97_Close(pCxt, AUXSTATE_ACLINK_AUDIO);
    }
}

void Codec_SetGpios(AudioContext *pCxt)
{
    Gpio_SetDir(52, GPIO_PIN_OUT);
    GPSR1 |= 1<< 20;
    DzDelay(50000);

    //reset pin
    Gpio_SetDir(113, GPIO_PIN_OUT);
    Gpio_SetAFR(113, GPIO_AS_AF2);

    CKEN |= CKEN2_AC97;
    OSCC |= 1<<3;
    DzDelay(10000);

    Gpio_SetDir(28, GPIO_PIN_IN);
    Gpio_SetAFR(28, GPIO_AS_AF1);
//    GPCR0 &= 1 << 28;//bitclock low
//    GPSR0 &=0<<28;//bitclock low
    GPCR0 |=  1 << 28;//bitclock low

    Gpio_SetDir(31, GPIO_PIN_OUT);
    Gpio_SetAFR(31, GPIO_AS_AF2);
    GPSR0 |= 1<<31;//andy
    DzDelay(500);

    GPSR0 |= 1<<28;//bitclock high

    Gpio_SetDir(30, GPIO_PIN_OUT);
    Gpio_SetAFR(30, GPIO_AS_AF2);

    Gpio_SetDir(29, GPIO_PIN_IN);
    Gpio_SetAFR(29, GPIO_AS_AF1);

    Gpio_SetDir(113, GPIO_PIN_OUT);
    Gpio_SetAFR(113, GPIO_AS_AF2);

    Gpio_SetDir(88, GPIO_PIN_OUT); //B_PA_SHUTDN
    GPCR2 |= 0x01000000; // Close it defaulty

    DzDelay(10000);
}

ECode Codec_Init(AudioContext *pCxt)
{
    Ac97_Write(AC97_POWER_CONTROL, 0L);
    DzDelay(10);

    // probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
    // be read zero.
    UInt16 codecID;
    Ac97_Read(AC97_RESET, &codecID);
    if (codecID & 0x8000) {
        return E_ACCESS_DENIED;
    }

//    UInt16 id1, id2;
//    Ac97_Read(AC97_VENDOR_ID1, &id1);
//    Ac97_Read(AC97_VENDOR_ID2, &id2);

#if 1
    // VREF should be always open for touch's normal operation
    Ac97_Write(0x26, 0x6700);
    Ac97_Write(0x24, 0x0000);
    Ac97_Write(0x3c, 0x1aff);
    Ac97_Write(0x3e, 0xffff);
#else
    Ac97_Write(0x26, 0x0000 );
    Ac97_Write(0x24, 0x0000 );
    Ac97_Write(0x3c, 0x0000);
    Ac97_Write(0x3e, 0x0000);
#endif

    //The following to set the 0x2a
    Ac97_Write(0x2a, 0x0411);
    /* p3 patch */
    Ac97_Write(0x16, 0x0603);
    /* bass control  */
    // Ac97_Write(0x08, 0x814b );
    /* 3D */

    Ac97_Write(0x20, 0x0f0f ); // Default close bass & treble
//    Ac97_Write(0x20, 0xa040 );
#ifdef MICBIAS_AUTOOPEN
    Ac97_Write(0x22, 0x0040 );
    Ac97_Write(0x26, 0x6300 ); // Open PR2 (input PGAs)
    Ac97_Write(0x3e, 0xbfff);
#endif
    /* ALC control */
    Ac97_Write(0x1c, 0x8f8f );
    Ac97_Write(0x62, 0x3eb5 );
#if 0
    //The following is added by Bill to set the 0x36
    Ac97_Write(0x36, 0xa503);
#endif

    // debug
    Ac97_Write(0x0c, 0xe606);

    return NOERROR;
}

#define GSR_PCR     (1 << 8)    /*  Primary Codec Ready */

void power_off_codec(void)
{
    GCR &= ~XLLP_AC97_GCR_LINK_OFF_MSK;
    CKEN &= ~(0x80000000);

    GCR |= 0x04;
    DzDelay(30);

    CKEN |= 0x00000004;

    while (!(GSR & GSR_PCR)) {
    }

    Ac97_Write(0x5a, 0x0100 );
    Ac97_Write(0x44, 0x0080 );

    Ac97_Write(0x36, 0x4523 );
    Ac97_Write(0x4c, 0xFFFE );
    Ac97_Write(0x4e, 0xFFFF );
    Ac97_Write(0x3e, 0xFFFF );
    Ac97_Write(0x26, 0x4100 );
    Ac97_Write(0x3c, 0xFBF3 );
    Ac97_Write(0x26, 0x4300 );
    Ac97_Write(0x3c, 0xfbff );
    Ac97_Write(0x26, 0x4700 );
    Ac97_Write(0x3c, 0xfeff );
    Ac97_Write(0x26, 0x4f00 );
    Ac97_Write(0x26, 0x6f00 );
    Ac97_Write(0x00, 0x0000 );

    GCR |= XLLP_AC97_GCR_LINK_OFF_MSK;

    CKEN |= 0x80000000;
    // Wait the AC-link shutdown is complete
    while (!(GSR & XLLP_AC97_GSR_ACOFFD_MSK));
    CKEN &= ~(0x80000000);
    CKEN &= ~0x00000004;

    GPDR1 |= (1<<20);
    GPCR1 |= (1<<20);
    return ;
}

// CALL_HANDFREE, Close power bits in Codec_CloseHandFreeCall
// CALL_BLUETOOTH, Close power bits in Codec_CloseBluetoothCall
// STATE_AUDIOOUT or STATE_AUDIOIN, close power bits here
//      actually the closing operation is already delay
//      in order to minimize pops and clicks
void Codec_Close(AudioContext *pCxt)
{
    UInt16 powerReg26, powerReg3c, powerReg3e;
    Boolean bCloseACLink = TRUE;
    DBGOUT(DBG_LOG, ("Codec_Close, state = 0x%x\n", pCxt->state));

    // NOTE: Reserved MIC BIAS bit (It will be dealt with later)
    // PR5, PR4 in reg 0x26, PLL in reg 0x3c will be closed in Ac97_Close
    // PR2 is used for MICBIAS, and it will be handled later
    // Touch power bits and VREF won't be considered here
    if (pCxt->state & STATE_CALL) {
        UInt32 callMode = CALL_GETMODE(pCxt->state);
        switch (callMode) {
            case CALL_HANDFREE:
                // Close PR1, PR0
                powerReg26 = 0x0300;
                // Dont't close VREF, SPKX
                powerReg3c = 0x18fd;
                // Dont't close SPKL, SPKR, LL
                powerReg3e = 0xbe3f;
                break;

            case CALL_HEADSET:
                // Close PR1, PR0
                powerReg26 = 0x0300;
                // Dont't close VREF, HPLX, HPRX, MX
                powerReg3c = 0x18f2;
                // Dont't close MONO, HPL, HPR, MOIN, mic PGAs(BUGBUG)
                powerReg3e = 0x99e0;
                break;

            case CALL_BLUETOOTH:
                // Close PR1
                powerReg26 = 0x0200;
                // Dont't close VXDAC, VREF, MX
                powerReg3c = 0x08fe;
                // Dont't close MONO, MOIN
                powerReg3e = 0x9fef;
                break;

            // case CALL_NORMAL:
            default:
                powerReg26 = 0x4300;
                powerReg3c = 0x18ff;
                powerReg3e = 0xbfff;
                break;
        }
        bCloseACLink = FALSE;
    }
    else {
        // Close all active bits
        powerReg26 = 0x4300;
        powerReg3c = 0x18ff;
        powerReg3e = 0xbfff;
        Codec_OpenAmplifier(pCxt, FALSE);
    }

#ifndef MICBIAS_AUTOOPEN
    // Close MIC BIAS
    if (!(pCxt->state & STATE_CALL)) {
        // Close MICBIAS
        Codec_ClearRegBits(0x22, 0x0040);
        powerReg26 |= 0x0400; // PR2
        powerReg3e |= 0x4000; // MICBIAS
    }
#endif

    // From document, may should do this
    // Mute DAC to mixer paths
    Codec_SetRegBits(0x0c, 0xe000);
    if (!((pCxt->state & STATE_CALL) &&
        (pCxt->state & (CALL_BLUETOOTH | CALL_HEADSET)))) {
        // Mutes MONO
        Codec_SetRegBits(0x08, 0x0080);
    }

    // Power Management
    Codec_SetPowerRegs(powerReg26, powerReg3c, powerReg3e);

    if (bCloseACLink) {
        // NOTICE:
        // If want to close ACLINK,
        // Must close disable PLL and set reg 0x26 PR4 individually here.
        Ac97_Close(pCxt, AUXSTATE_ACLINK_AUDIO);
    }

    DBGOUT(DBG_LOG, ("Codec_Close, auxState = 0x%x\n", pCxt->auxState));
}

// Write the sample rate value to the codec
// Parameters:
//      nSampleRate: the sample rate
ECode Codec_SetSampleRate(UInt32 rate, UInt32 dir)
{
    switch (rate) {
        case 8000:
        case 11025:
        case 12000:
        case 16000:
        case 22050:
        case 24000:
        case 32000:
        case 44100:
        case 48000:
            DBGOUT(DBG_LOG, ("Codec_SetSampleRate, dir: %d, rate: %d.\n",
                    dir, rate));
            break;
        default:
            DBGOUT(DBG_ERR, ("Codec_SetSampleRate, dir: %d, rate: %d(bad rate).\n",
                    dir, rate));
            return E_NOT_IMPLEMENTED;
    }
    Ac97_Write(dir == STATE_AUDIOOUT ? 0x2c : 0x32, rate);

    if (rate < 8000) {
        Ac97_Write(0x44, 0x0080);
    }
    else {
        // PLL_IN clock = 13Mhz, PLL_OUT clock = 98.304Mhz
        // N[3:0] = 7h, K[21:0] = 23f488h
        Ac97_Write(0x46,  0x7408 );
        Ac97_Write(0x46,  0x7418 );
        Ac97_Write(0x46,  0x7424 );
        Ac97_Write(0x46,  0x743f );
        Ac97_Write(0x46,  0x7443 );
        Ac97_Write(0x46,  0x7452 );
        Ac97_Write(0x46,  0x7460 );
        Ac97_Write(0x46,  0x7470 );
        Ac97_Write(0x44,  0x0000);
    }

    return NOERROR;
}

#define CODECVOLUME_LEVEL 16
// Used for speaker and mono
const UInt16 c_speakerVolumeTable[] = {
    //0,  1,  2,  3,  4,  5,  6,  7, 8, 9, 10, 11, 12, 13, 14, 15     input
     31, 18, 15, 12, 10,  8,  7,  6, 5, 4,  3,  3,  2,  2,  1,  0  // ouput
    };
// Used for headset
const UInt16 c_headsetVolumeTable[] = {
    //0,  1,  2,  3,  4,  5,  6,  7, 8, 9, 10, 11, 12, 13, 14, 15     input
     31, 20, 17, 14, 12, 10,  9,  8, 7, 6,  5,  4,  3,  2,  1,  0  // ouput
    };
#define GETCODECVOLUME_SPEAKER(volume) \
    (c_speakerVolumeTable[volume / CODECVOLUME_LEVEL])
#define GETCODECVOLUME_HEADSET(volume) \
    (c_headsetVolumeTable[volume / CODECVOLUME_LEVEL])
#define GETCODECVOLUME_MONO(volume) \
    (c_speakerVolumeTable[volume / CODECVOLUME_LEVEL])

ECode Codec_SetVolume(AudioContext *pCxt, Byte left, Byte right, UInt32 volType)
{
    UInt16 volume = 0, RegLeft, RegRight;
    ECode ec = NOERROR;

    DBGOUT(DBG_LOG, ("Codec_SetVolume, left: 0x%x, right: 0x%x, type: %d\n",
        left, right, volType));
    DBGOUT(DBG_LOG, ("Codec_SetVolume, state = 0x%x, auxState = 0x%x\n",
        pCxt->state, pCxt->auxState));

#ifdef DBUG_AUDIO
    // When not mute a output device:
    // If headset is closed or it isn't plugged, can't enter this function.
    // If bluetooth headset is closed, can't enter this function.
    if (left != 0 || right != 0) {
        if (VOLUMETYPE_HEADSET == volType) {
            assert((pCxt->state & (AUXSTATE_HEADSETPLUG | HEADSET_OUT_CLOSE))
                == AUXSTATE_HEADSETPLUG);
        }
#ifdef BLUETOOTH_PLAY_RECORD_ENABLE
        if (VOLUMETYPE_BTHEADSET == volType) {
            assert(pCxt->state & BTHEADSET_OPEN);
        }
    }
#endif
#endif

    // BUGBUG: The channels are upside down in hardware
    if (VOLUMETYPE_SPEAKER == volType) {
        // Speaker can only support 1 channel to output now.
        // Use the maximal volume of 2 channels to set!
        // So the speaker can talk normally
        // when any of the two channels are set rightly.
        if (left > right)
            right = left;
        else
            left = right;
    }
    else {
        Byte temp;
        temp = left;
        left = right;
        right = temp;
    }

    switch (volType) {
        case VOLUMETYPE_SPEAKER:
            if (!(pCxt->auxState & AUXSTATE_SPEAKER_WORKING)) {
                // When open PGA set the lowest volume
                // in order to minimise pops and clicks
                // Ac97_Write(0x02, 0xbfbf);

                Codec_OpenAmplifier(pCxt, TRUE);

                // Power Management: Open speaker mixer
                Codec_ClearRegBits(0x3c, 0x0002);
                // Power Management: Open SPKL and SPKR output PGA
                Codec_ClearRegBits(0x3e, 0x0180);
                // Open DAC to speaker mixer path
                Codec_ClearRegBits(0x0c, 0x4000);
                // INV2: SPKMIX
                Codec_WriteRegValue(0x1e, 0x0800, 0xfe00);
                //Ac97_Write(0x1e, 0x0800);
                // SPKL & SPKR PGA input select (SPKMIX & INV2)
                Codec_WriteRegValue(0x1c, 0x1c00, 0x3f00);
                // Codec_WriteRegValue(0x1c, 0x2400, 0x3f00);

                pCxt->auxState |= AUXSTATE_SPEAKER_WORKING;
            }

            if (left == 0 && right == 0) {
                // Close speaker later to get better speaker voice
                // Codec_OpenAmplifier(pCxt, FALSE);
                // Mute speaker
                Codec_SetRegBits(0x02, 0x8080);
                // Mute DAC to speaker mixer path
                Codec_SetRegBits(0x0c, 0x4000);
                // PGA input select
                Codec_WriteRegValue(0x1c, 0x3c00, 0x3c00);
                pCxt->auxState &= ~AUXSTATE_SPEAKER_WORKING;
            }
            else {
                assert((pCxt->state & AUDIOOUT_SPEAKER) ||
                        (CALL_GETMODE(pCxt->state) == CALL_HANDFREE));
                RegLeft   = GETCODECVOLUME_SPEAKER(left);
                RegRight  = GETCODECVOLUME_SPEAKER(right);
                volume = RegRight | (RegLeft << 8);
                if( left == 0 ) volume |= 0x8000; //mute
                if( right == 0 ) volume |= 0x0080; //mute
                Ac97_Write(0x02, volume);
            }
            break;

        case VOLUMETYPE_HEADSET:
            if (!(pCxt->auxState & AUXSTATE_HEADSETOUT_WORKING)) {
                // When open PGA set the lowest volume
                // in order to minimise pops and clicks
                // Ac97_Write(0x04, 0xbfbf);

                // Power Management: Open left and right headphone mixer
                Codec_ClearRegBits(0x3c, 0x000c);
                // Power Management: Open HPL and HPR output PGA
                Codec_ClearRegBits(0x3e, 0x0600);
                // Open DAC to headphone mixer path
                Codec_ClearRegBits(0x0c, 0x8000);
                // HPL & HPR PGA input select (HPMIXL & HPMIXR)
                Codec_WriteRegValue(0x1c, 0x00a0, 0x00f0);
                pCxt->auxState |= AUXSTATE_HEADSETOUT_WORKING;
            }

            if (left == 0 && right == 0) {
                // Mute headset
                Codec_SetRegBits(0x04, 0x8080);
                // Mute DAC to headphone mixer path
                Codec_SetRegBits(0x0c, 0x8000);
                // PGA input select
                Codec_WriteRegValue(0x1c, 0x00f0, 0x00f0);
                pCxt->auxState &= ~AUXSTATE_HEADSETOUT_WORKING;
            }
            else {
                assert(pCxt->auxState & AUXSTATE_HEADSETPLUG); //NOTE
                assert((pCxt->state & AUDIOOUT_HEADSET) ||
                        (CALL_GETMODE(pCxt->state) == CALL_HEADSET));
                RegLeft   = GETCODECVOLUME_HEADSET(left);
                RegRight  = GETCODECVOLUME_HEADSET(right);
                volume = RegRight | (RegLeft << 8);
                if( left == 0 ) volume |= 0x8000; //mute
                if( right == 0 ) volume |= 0x0080; //mute
                Ac97_Write(0x04, volume);
            }
            break;

#ifdef BLUETOOTH_PLAY_RECORD_ENABLE
        case VOLUMETYPE_BTHEADSET:
          // For bluetooth headset
            if (!(pCxt->auxState & AUXSTATE_BLUETOOTHOUT_WORKING)) {
                CodecOpenPCMLink();
                // Open the pass from AC97LINK to PCMLINK
                // From AC97 link--DAC---MONO mixer---ADC---PCM link
                //
                // Power Management: Open ADCs and record mux PGA
                // (DAC already open in Codec_OpenAc97DAC)
                Codec_ClearRegBits(0x26, 0x0100);
                // Power Management: Open left and right ADC, mono mixer
                // (left and right DAC already open in Codec_OpenAc97DAC)
                Codec_ClearRegBits(0x3c, 0x0031);
                // Power Management: Open MONO output PGA
                Codec_ClearRegBits(0x3e, 0x2000);

                // Open DAC to MONO mixer
                Codec_ClearRegBits(0x0c, 0x2000);
                // Recorder selecter (MONOMIX), without 20db gain
                Codec_WriteRegValue(0x14,  0x0036, 0x007f);
                // Open Audio ADC
                Codec_ClearRegBits(0x12, 0x8000);

                pCxt->auxState |= AUXSTATE_BLUETOOTHOUT_WORKING;
            }

            if (left == 0 && right == 0) {
                // Mute audio ADC input
                Codec_SetRegBits(0x12, 0x8000);
                // Mute DAC to MONO mixer path
                Codec_SetRegBits(0x0c, 0x2000);
                // Recorder selecter (Reserved)
                Codec_WriteRegValue(0x14, 0x003f, 0x007f);
                pCxt->auxState &= ~AUXSTATE_BLUETOOTHOUT_WORKING;
            }
            else {
                assert((pCxt->state & AUDIOOUT_BLUETOOTH) ||
                        (CALL_GETMODE(pCxt->state) == CALL_BLUETOOTH));

                // Set bluetooth volume, actually set ADC gain
                // volume = RegRight | (RegLeft << 8);
                // Ac97_Write(0x12, volume);

                // Bluetooth headset volume should be set by AT command,
                // using bluetooth stack software.
                Ac97_Write(0x12, 0x0000);
            }
            break;
#endif

        case VOLUMETYPE_MONO:
          // Only left volume is used, because only 1 channel for MONO
            volume = GETCODECVOLUME_MONO(left);
            Codec_WriteRegValue(0x08, volume, 0x001f);
            break;

        default:
            ec = E_INVALID_ARGUMENT;
            break;
    }

// Special debug message
#if 0
    UInt16 volume;
    Ac97_Read(0x04, &volume);
    cprintf("Headset volume: 0x%x\n", volume);
    Ac97_Read(0x02, &volume);
    cprintf("Speaker volume: 0x%x\n", volume);
    Ac97_Read(0x0c, &volume);
    cprintf("DAC input gain: 0x%x\n", volume);
#endif

    return ec;
}

// Set register 12h to change ADC gain
// We can also set register 22h to change MICA/MICB preamp
ECode Codec_SetRecordVolume(AudioContext *pCxt, Byte left, Byte right)
{
    UInt16 volume = 0, RegLeft, RegRight;

    DBGOUT(DBG_LOG, ("Codec_SetRecordVolume, left: 0x%x, right: 0x%x\n",
        left, right));

    // Convert user input value to register value
    // [0, 255] -> [0, 15]
    RegLeft = left / 16;
    RegRight = right / 16;

    // Bit15 is cleared to open audio ADC input
    volume = (RegLeft << 8) | RegRight;

    Ac97_Write(0x12, volume);

    return NOERROR;
}

ECode Codec_SetCallVolume(AudioContext *pCxt, Byte volume)
{
    ECode ec = NOERROR;
    UInt32 callMode = CALL_GETMODE(pCxt->state);

    DBGOUT(DBG_LOG, ("Codec_SetCallVolume, volume: 0x%x, state: 0x%x\n",
        volume, pCxt->state));
    
    switch (callMode) {
        case CALL_NORMAL:
            break;

        case CALL_HANDFREE:
            volume = GETCODECVOLUME_SPEAKER(volume);
            ec = Ac97_Write(0x02, (volume << 8) | volume);
            break;

        case CALL_HEADSET:
            volume = GETCODECVOLUME_HEADSET(volume);
            Ac97_Write(0x04, (volume << 8) | volume);
            Codec_SetVolume(pCxt, 0xff, 0xff, VOLUMETYPE_MONO);
            break;

        case CALL_BLUETOOTH:
            break;

        default:
            ec = E_INVALID_ARGUMENT;
            break;
    }

    return ec;
}

void Codec_OpenAmplifier(AudioContext *pCxt, Boolean bOpen)
{
    if (bOpen) {
        GPSR2 |= 0x01000000; // Open
        if (pCxt->auxState & AUXSTATE_AMPLIFIER_OPEN) {
        }
        else {
            pCxt->auxState |= AUXSTATE_AMPLIFIER_OPEN;
            DzSleep(4, NULL); // Delay some time to let speaker work normally
        }
    }
    else {
        GPCR2 |= 0x01000000; // Close
        pCxt->auxState &= ~AUXSTATE_AMPLIFIER_OPEN;
    }
}

void EnableEarpBtnDetection(Boolean enable);
void Codec_GetHeadsetStatus(AudioContext *pCxt)
{
    if (GPLR0 & (1 << 1)) {
        // headset plug
        pCxt->auxState |= AUXSTATE_HEADSETPLUG;
#ifdef HEADSET_AUTOOPEN
        pCxt->state |= AUDIOOUT_HEADSET;
#endif
    }
    else {
        // headset remove
        pCxt->auxState &= ~AUXSTATE_HEADSETPLUG;
#ifdef HEADSET_AUTOOPEN
        pCxt->state &= ~AUDIOOUT_HEADSET;
#endif
    }
}

unsigned long headsetstamp = 0;
Boolean VerifyHeadset(AudioContext *pCxt)
{
    if (OSCR - headsetstamp < 3250 * 200/*ms*/) {//Ignore
        //STTHR = 'i';
        //cputs("============Ignore Headset remove.\n");
        return FALSE;
    }

    headsetstamp = OSCR;

    if (GPLR0 & (1 << 1)) {
        // maybe headset plug
        if (pCxt->auxState & AUXSTATE_HEADSETPLUG) {
            //STTHR = 'p';
            //cputs("=============Already Headset plug.\n");
            return FALSE;
        }
    }
    else {
        // maybe headset remove
        if ((pCxt->auxState & AUXSTATE_HEADSETPLUG) == 0) {
            //STTHR = 'r';
            //cputs("============Already Headset remove.\n");
            return FALSE;
        }
    }
    return TRUE;
}

void PoNotifyEvent(int evNum, void *pParameter);
void CDECL HeadsetIsr(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    AudioContext *pCxt = (AudioContext *)pvDevice;

    pCxt->auxState |= AUXSTATE_HEADSETPLUGISR;

    Boolean bret = VerifyHeadset(pCxt);

    GEDR0 |= (1 << 1);           // clear GPIO status

    if (!bret) return ;

    Codec_GetHeadsetStatus(pCxt);

    EmIsrParameter Param;
    Param.nParameter1 = Param.nParameter2 = 0;
    Param.nParameter3 = Param.nParameter4 = 0;

    if (pCxt->auxState & AUXSTATE_HEADSETPLUG) {
        // headset plug
        //STTHR = '+';
        //cputs("\nHeadset plug.\n"); // DEBUG MESSAGE
        EnableEarpBtnDetection(TRUE);
        PoNotifyEvent(120, &Param);
    }
    else {
        // headset remove
        //STTHR = '-';
        //cputs("Headset remove.\n"); // DEBUG MESSAGE
        EnableEarpBtnDetection(FALSE);
        PoNotifyEvent(121, &Param);
    }
    NotifyByIsr(EVENT_INPUT, 0, NULL);

    return ;
}

#define IRQ_GPIO1 9
ECode Codec_InitHeadset(AudioContext *pCxt)
{
    // Headset plug & remove interrupt
    DzRegisterIsr(IRQ_GPIO1, IPL7, (isr_t)HeadsetIsr, (void *)pCxt);
    Gpio_SetDir(1, GPIO_PIN_IN);
    GRER0 |= 1 << 1;
    GFER0 |= 1 << 1;

    Codec_GetHeadsetStatus(pCxt);

    return NOERROR;
}

void HeadSetSleep()
{
    //if(GPLR0 & (1 << 1))
    {
        EnableEarpBtnDetection(FALSE);
    }
}

void _HeadsetWake();
void HeadSetWake(Boolean isFromSleep)
{
    UInt32 eNum;
    EmIsrParameter Param;
    Param.nParameter1 = Param.nParameter2 = 0;
    Param.nParameter3 = Param.nParameter4 = 0;
    _HeadsetWake();
    if (GPLR0 & (1 << 1)) {
        cputs("HeadSetWake\n");
        headsetstamp = OSCR0;
        EnableEarpBtnDetection(TRUE);
        eNum = 120;
    }
    else {
        EnableEarpBtnDetection(FALSE);
        eNum = 121;
    }

    if (isFromSleep) {
        PoNotifyEvent(eNum, &Param);
    }
    return ;
}
